package com.michaelbulava.TFS.vcs.internal.providers.internal;

import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.vcs.AbstractVcsHelper;
import com.intellij.openapi.vcs.VcsKey;
import com.intellij.openapi.vcs.annotate.AnnotationSourceSwitcher;
import com.intellij.openapi.vcs.annotate.FileAnnotation;
import com.intellij.openapi.vcs.annotate.LineAnnotationAspect;
import com.intellij.openapi.vcs.annotate.LineAnnotationAspectAdapter;
import com.intellij.openapi.vcs.history.VcsFileRevision;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.GuiUtils;
import com.intellij.util.text.DateFormatUtil;
import com.michaelbulava.TFS.Messages;
import com.michaelbulava.TFS.vcs.RevisionChangedListener;
import com.michaelbulava.TFS.vcs.TFSVcs;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace;

import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.*;

/**
 * Created by MichaelA on 8/18/2015.
 */
public class TFSFileAnnotation extends FileAnnotation {
    private final TFSVcs myVcs;
    private final Workspace myWorkspace;
    private final String myAnnotatedContent;
    private final VcsFileRevision myLineRevisions[];
    private final VirtualFile myFile;
    private final LineAnnotationAspect REVISION_ASPECT;
    private final LineAnnotationAspect DATE_ASPECT;
    private final LineAnnotationAspect AUTHOR_ASPECT;

    private final RevisionChangedListener myListener = new RevisionChangedListener() {

        public void revisionChanged()
        {
            try
            {
                GuiUtils.runOrInvokeAndWait(new Runnable() {

                                                public void run() {
                                                    close();
                                                }


                                            }
                );
            }
            catch(InvocationTargetException e) { }
            catch(InterruptedException e) { }
        }


    };

    private static final Comparator REVISION_COMPARATOR = new Comparator<VcsFileRevision>() {

        public int compare(VcsFileRevision revision1, VcsFileRevision revision2) {
            return revision1.getRevisionNumber().compareTo(revision2.getRevisionNumber());
        }
    };



    public TFSFileAnnotation(TFSVcs vcs, Workspace workspace, String annotatedContent, VcsFileRevision[] lineRevisions,
                             VirtualFile file) {
        super(vcs.getProject());

        REVISION_ASPECT = new TFSAnnotationAspect(TFSAnnotationAspect.REVISION, false) {
            @Override
            public String getValue(int lineNumber) {
                if (lineNumber < myLineRevisions.length)
                    return myLineRevisions[lineNumber].getRevisionNumber().asString();
                else
                    return "";
            }
        };

        DATE_ASPECT = new TFSAnnotationAspect(TFSAnnotationAspect.DATE, true) {
            @Override
            public String getValue(int lineNumber) {
                if (lineNumber < myLineRevisions.length)
                    return DateFormatUtil.formatPrettyDate(myLineRevisions[lineNumber].getRevisionDate());
                else
                    return "";
            }
        };

        AUTHOR_ASPECT = new TFSAnnotationAspect(TFSAnnotationAspect.AUTHOR, true) {
            @Override
            public String getValue(int lineNumber) {
                if(lineNumber < myLineRevisions.length)
                    return myLineRevisions[lineNumber].getAuthor();
                else
                    return "";
            }
        };

        myVcs = vcs;
        myWorkspace = workspace;
        myAnnotatedContent = annotatedContent;
        myLineRevisions = lineRevisions;
        myFile = file;
        myVcs.addRevisionChangedListener(myListener);
    }

    @Override
    public void dispose(){
        myVcs.removeRevisionChangedListener(myListener);
    }

    @Override
    public String getAnnotatedContent(){
        return myAnnotatedContent;
    }

    @Override
    public LineAnnotationAspect[] getAspects(){
        return new LineAnnotationAspect[]{
                REVISION_ASPECT, DATE_ASPECT, AUTHOR_ASPECT
        };
    }

    @Override
    public String getToolTip(int lineNumber){
        if(lineNumber < myLineRevisions.length){
            String comittMessage = myLineRevisions[lineNumber].getCommitMessage() != null ?
                    myLineRevisions[lineNumber].getCommitMessage() :
                    Messages.getString("TFSFileAnnotation.NoComment");
            return MessageFormat.format(Messages.getString("TFSFileAnnotation.ToolTipFormat"),
                    new Object[]{ myLineRevisions[lineNumber].getRevisionNumber().asString(), comittMessage  });
        } else {
            return "";
        }
    }

    @Override
    public VcsRevisionNumber getLineRevisionNumber(int lineNumber){
        if (lineNumber < myLineRevisions.length)
            return myLineRevisions[lineNumber].getRevisionNumber();
        else
            return null;
    }

    @Override
    public Date getLineDate(int lineNumber){
        if (lineNumber < myLineRevisions.length)
            return myLineRevisions[lineNumber].getRevisionDate();
        else
            return null;
    }

    @Override
    public VcsRevisionNumber originalRevision(int lineNumber){
        return getLineRevisionNumber(lineNumber);
    }

    @Override
    public List<VcsFileRevision> getRevisions(){
        Set set = new HashSet(Arrays.asList(myLineRevisions));
        List<VcsFileRevision> result = new ArrayList<VcsFileRevision>(set);
        Collections.sort(result, REVISION_COMPARATOR);
        return result;
    }

    @Override
    public boolean revisionsNotEmpty(){
        return myLineRevisions.length > 0;
    }

    @Override
    public AnnotationSourceSwitcher getAnnotationSourceSwitcher(){ return null; }

    @Override
    public int getLineCount(){
        return myLineRevisions.length;
    }

    @Override
    public VcsRevisionNumber getCurrentRevision(){
        return null;
    }

    @Override
    public VcsKey getVcsKey(){
        return TFSVcs.getKey();
    }

    @Override
    public VirtualFile getFile(){
        return myFile;
    }




    private abstract class TFSAnnotationAspect extends LineAnnotationAspectAdapter{
        public TFSAnnotationAspect(String id, boolean showByDefault){
            super(id, showByDefault);
        }

        @Override
        protected void showAffectedPaths(int lineNum){
            if (lineNum < myLineRevisions.length){
                VcsFileRevision revision = myLineRevisions[lineNum];
                int changeset = ((com.intellij.openapi.vcs.history.VcsRevisionNumber.Int)revision.getRevisionNumber())
                        .getValue();
                final CommittedChangeList changeList = new TFSChangeList(myWorkspace, changeset, revision.getAuthor(),
                        revision.getRevisionDate(), revision.getCommitMessage(), myVcs);
                String changesetString = ((VcsRevisionNumber.Int)revision.getRevisionNumber()).asString();
                String progress = MessageFormat.format("Loading changeset {0}...", new Object[]{
                        changesetString
                });

                ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {

                    public void run()
                    {
                        changeList.getChanges();
                    }


                }
                        , progress, false, myVcs.getProject());
                String title = MessageFormat.format("Changeset {0}", new Object[] {
                        changesetString
                });
                AbstractVcsHelper.getInstance(myVcs.getProject()).showChangesListBrowser(changeList, title);
            }
        }
    }
}
